package server;

import java.net.InetAddress;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import server.data.User;

/**
 * Encapsulates all necessary operations which have to be done which Auctions.
 *
 * @author Andreas Mair
 */
public final class UserManager {

    private static final String NEW_LINE=System.getProperty("line.separator");
    private static UserManager instance;
    private final Map<String, User> users;

    private UserManager() {
        users = new HashMap<String, User>();
    }

    public synchronized static UserManager getInstance() {
        if (instance == null) {
            instance = new UserManager();
        }
        return instance;
    }

    /**
     * Logs a User in (his ClientAddress will be set)
     *
     * @param strName User to log in
     * @param iaClientAddress InetAddress of client
     * @param iPClientPort int UDP-Port to send UDP-Messages to this User/Client
     * @return User object or null if the user was already logged in (somewhere)
     */
    public synchronized User login(String strName, InetAddress iaClientAddress, int iPClientPort) {
        User user = null;
        user = users.get(strName);
        if (user != null && user.getIAClientAddress() != null) {
            return null;
        }
        if (user == null) {
            user = new User(strName);
            users.put(strName, user);
        }
        user.setIAClientAddress(iaClientAddress);
        user.setiPClientPort(iPClientPort);
        return user;
    }

    /**
     * Sends Notifications to the user if it has some
     *
     * @param user to send Notifications to
     */
    public synchronized void sendNotifications(User user) {
        List<String> notifications = new ArrayList<String>(user.getArrNotifications());
        user.getArrNotifications().clear();
        for (String str : notifications) {
            this.sendUDPMessage(user, str);
        }
    }

    /**
     * Logs a User out (his ClientAddress will be set to null), Clientaddress
     * and Clientport to validate that the user was logged in at that Client
     * (shouldn't be possible anyway)
     *
     * @param strName Usert to log out
     * @param iaClientAddress InetAddress of client used by this User/Client
     * @return User object or null if the user was not logged in at that Client
     */
    public synchronized User logout(String strName, InetAddress iaClientAddress, int iPClientPort) {
        User user = users.get(strName);
        if (user == null || user.getIAClientAddress() == null || !user.getIAClientAddress().equals(iaClientAddress) || user.getiPClientPort() != iPClientPort) {
            return null;
        } else {
            user.setIAClientAddress(null);
            user.setiPClientPort(0);
        }
        return user;
    }

    public synchronized User getUser(String username) {
        return users.get(username);
    }

    /**
     * Used to send TCP-Messages to Users in Assignmet 3
     *
     * @param user User to send a Message to
     * @param message String Message to send
     */
    public synchronized void addNotification(User user, String message) {
        if (user != null) {
            user.getArrNotifications().add(message);
        }
    }

    public List<String> getArrNotifications(User user) {
        List ret = null;
        if (user != null) {
            ret = user.getArrNotifications();
        }
        return ret;
    }

    /**
     * Sends a UDP-Message to a User, the message is sent immediately if the
     * user is online, otherwise it is added to arrNotifications and sent, when
     * the User logs in
     *
     * @param user User to send a Message to
     * @param message String Message to send
     */
    public synchronized void sendUDPMessage(User user, String message) {
        if (user != null) {
            try {
                String strNotSent = UDPMessenger.getInstance().sendMessage(user.getIAClientAddress(), user.getiPClientPort(), message);
                if (strNotSent != null) {
                    user.getArrNotifications().add(strNotSent);
                }
            } catch (SocketException e) {
                System.out.println("Error while sending '" + message + "' to '" + user + "'!");
                user.getArrNotifications().add(message);
            }
        }
    }

    /**
     * Returns the number of the logged on Users (a User is logged on, if his
     * ClientAdress!=null)
     *
     * @return number of logged on Users
     */
    public synchronized int getLoggedOnUserCount() {
        int iRet = 0;
        for (User user : users.values()) {
            if (user.getIAClientAddress() != null) {
                iRet++;
            }
        }
        return iRet;
    }
    
    /**
     * Returns a list of all currently logged in clients with IP and port.
     * @return 
     */
    public synchronized String getClientList() {
        String ip = "";
        String output = "";
        for (User user : users.values()) {
            if (user.getIAClientAddress() != null) {
                if (output.length() > 0) {
                    output += NEW_LINE;
                }
                output += user.getIAClientAddress().toString().substring(1) + ":" + user.getiPClientPort() + " - " + user.toString();
            }
        }
        return output;
    }
}
